// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) LeonXiang
// All Rights Reserved.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WPF.UI.Drawing.Controls
{
    public class DrawingCanvas : Canvas
    {
        // 定义依赖属性，用于接收外部传入的图形数据
        public static readonly DependencyProperty ShapesProperty =
            DependencyProperty.Register("Shapes", typeof(List<DrawingShape>), typeof(DrawingCanvas), new PropertyMetadata(new List<DrawingShape>(), OnShapesChanged));

        public List<DrawingShape> Shapes
        {
            get { return (List<DrawingShape>)GetValue(ShapesProperty); }
            set { SetValue(ShapesProperty, value); }
        }

        // 定义绘制模式依赖属性
        public static readonly DependencyProperty DrawingModeProperty =
            DependencyProperty.Register("DrawingMode", typeof(ShapeType), typeof(DrawingCanvas), new PropertyMetadata(ShapeType.Rectangle));

        public ShapeType DrawingMode
        {
            get { return (ShapeType)GetValue(DrawingModeProperty); }
            set { SetValue(DrawingModeProperty, value); }
        }

        // 定义当前图片路径依赖属性
        public static readonly DependencyProperty CurrentImagePathProperty =
            DependencyProperty.Register("CurrentImagePath", typeof(string), typeof(DrawingCanvas), new PropertyMetadata(""));

        public string CurrentImagePath
        {
            get { return (string)GetValue(CurrentImagePathProperty); }
            set { SetValue(CurrentImagePathProperty, value); }
        }

        private static void OnShapesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var canvas = (DrawingCanvas)d;
            canvas.InvalidateVisual();
        }

        private DrawingShape draggedShape;
        private Point startDragPoint;
        private Point startDrawingPoint;
        private DrawingShape currentDrawingShape;

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);
            var mousePos = e.GetPosition(this);
            bool isClickOnShape = Shapes.Exists(shape => shape.Bounds.Contains(mousePos));

            if (!isClickOnShape)
            {
                CaptureMouse();
                startDrawingPoint = mousePos;
                switch (DrawingMode)
                {
                    case ShapeType.Rectangle:
                        currentDrawingShape = new DrawingShape
                        {
                            Type = ShapeType.Rectangle,
                            Bounds = new Rect(mousePos.X, mousePos.Y, 0, 0),
                            Fill = Brushes.Black
                        };
                        Shapes.Add(currentDrawingShape);
                        break;
                    case ShapeType.Circle:
                        currentDrawingShape = new DrawingShape
                        {
                            Type = ShapeType.Circle,
                            Bounds = new Rect(mousePos.X, mousePos.Y, 0, 0),
                            Fill = Brushes.Black
                        };
                        Shapes.Add(currentDrawingShape);
                        break;
                    case ShapeType.Line:
                        currentDrawingShape = new DrawingShape
                        {
                            Type = ShapeType.Line,
                            StartPoint = mousePos,
                            EndPoint = mousePos,
                            Fill = Brushes.Black
                        };
                        Shapes.Add(currentDrawingShape);
                        break;
                    case ShapeType.Point:
                        currentDrawingShape = new DrawingShape
                        {
                            Type = ShapeType.Point,
                            Bounds = new Rect(mousePos.X, mousePos.Y, 2, 2),
                            Fill = Brushes.Black
                        };
                        Shapes.Add(currentDrawingShape);
                        currentDrawingShape = null;
                        InvalidateVisual();
                        break;
                    case ShapeType.Image:
                        if (!string.IsNullOrEmpty(CurrentImagePath))
                        {
                            currentDrawingShape = new DrawingShape
                            {
                                Type = ShapeType.Image,
                                Bounds = new Rect(mousePos.X, mousePos.Y, 0, 0),
                                ImagePath = CurrentImagePath
                            };
                            Shapes.Add(currentDrawingShape);
                        }
                        break;
                }
            }
            else
            {
                draggedShape = Shapes.Find(shape => shape.Bounds.Contains(mousePos));
                if (draggedShape != null)
                {
                    startDragPoint = mousePos;
                    CaptureMouse();
                }
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (IsMouseCaptured)
            {
                var currentMousePos = e.GetPosition(this);
                if (draggedShape != null)
                {
                    var offsetX = currentMousePos.X - startDragPoint.X;
                    var offsetY = currentMousePos.Y - startDragPoint.Y;
                    var newBounds = draggedShape.Bounds;
                    newBounds.X += offsetX;
                    newBounds.Y += offsetY;
                    draggedShape.Bounds = newBounds;
                    if (draggedShape.Type == ShapeType.Line)
                    {
                        draggedShape.StartPoint = new Point(draggedShape.StartPoint.X + offsetX, draggedShape.StartPoint.Y + offsetY);
                        draggedShape.EndPoint = new Point(draggedShape.EndPoint.X + offsetX, draggedShape.EndPoint.Y + offsetY);
                    }
                    startDragPoint = currentMousePos;
                }
                else if (currentDrawingShape != null)
                {
                    switch (DrawingMode)
                    {
                        case ShapeType.Rectangle:
                            currentDrawingShape.Bounds = new Rect(
                                Math.Min(startDrawingPoint.X, currentMousePos.X),
                                Math.Min(startDrawingPoint.Y, currentMousePos.Y),
                                Math.Abs(currentMousePos.X - startDrawingPoint.X),
                                Math.Abs(currentMousePos.Y - startDrawingPoint.Y));
                            break;
                        case ShapeType.Circle:
                            var centerX = (startDrawingPoint.X + currentMousePos.X) / 2;
                            var centerY = (startDrawingPoint.Y + currentMousePos.Y) / 2;
                            var radius = Math.Sqrt(Math.Pow(currentMousePos.X - startDrawingPoint.X, 2) + Math.Pow(currentMousePos.Y - startDrawingPoint.Y, 2)) / 2;
                            currentDrawingShape.Bounds = new Rect(centerX - radius, centerY - radius, radius * 2, radius * 2);
                            break;
                        case ShapeType.Line:
                            currentDrawingShape.EndPoint = currentMousePos;
                            break;
                        case ShapeType.Image:
                            currentDrawingShape.Bounds = new Rect(
                                Math.Min(startDrawingPoint.X, currentMousePos.X),
                                Math.Min(startDrawingPoint.Y, currentMousePos.Y),
                                Math.Abs(currentMousePos.X - startDrawingPoint.X),
                                Math.Abs(currentMousePos.Y - startDrawingPoint.Y));
                            break;
                    }
                }
                InvalidateVisual();
            }
        }

        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);
            if (IsMouseCaptured)
            {
                ReleaseMouseCapture();
                draggedShape = null;
                currentDrawingShape = null;
            }
        }

        protected override void OnRender(DrawingContext dc)
        {
            base.OnRender(dc);

            foreach (var shape in Shapes)
            {
                switch (shape.Type)
                {
                    case ShapeType.Rectangle:
                        dc.DrawRectangle(shape.Fill, null, shape.Bounds);
                        break;
                    case ShapeType.Circle:
                        var center = new Point(shape.Bounds.X + shape.Bounds.Width / 2, shape.Bounds.Y + shape.Bounds.Height / 2);
                        var radiusX = shape.Bounds.Width / 2;
                        var radiusY = shape.Bounds.Height / 2;
                        dc.DrawEllipse(shape.Fill, null, center, radiusX, radiusY);
                        break;
                    case ShapeType.Line:
                        dc.DrawLine(new Pen(shape.Fill, 2), shape.StartPoint, shape.EndPoint);
                        break;
                    case ShapeType.Point:
                        dc.DrawEllipse(shape.Fill, null, new Point(shape.Bounds.X + shape.Bounds.Width / 2, shape.Bounds.Y + shape.Bounds.Height / 2), 1, 1);
                        break;
                    case ShapeType.Image:
                        try
                        {
                            var bitmap = new BitmapImage(new Uri(shape.ImagePath));
                            dc.DrawImage(bitmap, shape.Bounds);
                        }
                        catch (Exception ex)
                        {
                            // 处理图片加载异常
                            Console.WriteLine($"Failed to load image: {ex.Message}");
                        }
                        break;
                }
            }
        }
    }
}
